Skip to content

Adds datastore_index_scan_point_bsatn to C# Runtime#3909

Merged
rekhoff merged 6 commits intomasterfrom
rekhoff/add-datastore-index-scan-point-bsatn
Dec 20, 2025
Merged

Adds datastore_index_scan_point_bsatn to C# Runtime#3909
rekhoff merged 6 commits intomasterfrom
rekhoff/add-datastore-index-scan-point-bsatn

Conversation

@rekhoff
Copy link
Contributor

@rekhoff rekhoff commented Dec 19, 2025

Description of Changes

To resolve #3875, we added exact-match unique index point lookup support to the C# bindings by introducing and using datastore_index_scan_point_bsatn.

Previously, generated unique index Find() was (in at least one codepath) implemented as:

  • A range scan (datastore_index_scan_range_bsatn) over a BTree bound, then
  • SingleOrDefault() to collapse the results into a single row.
    When the scan is empty, SingleOrDefault() returns default(T). For value-type rows this can manifest as a default-initialized row instead of “missing”, which is what surfaced as “default-ish row” behavior in views.

Using datastore_index_scan_point_bsatn makes the C# implementation match Rust semantics more closely by performing an exact point lookup and returning:

  • null when no rows are found
  • the row when exactly one row is found
  • (defensively) an error if >1 row is returned (unique index invariant violation)
    Similarly, datastore_delete_by_index_scan_point_bsatn was added and used so deletes-by-unique-key are also exact-match point operations rather than range deletes.

Runtime updates were made to utilize point scan in FindSingle(key) and in both mutable/read-only unique-index paths.

To keep this non-breaking for existing modules, codegen now detects whether the table row is a struct or a class and chooses the appropriate base type:

  • Struct rows: Find() returns Row? (Nullable<Row>).
  • Class rows: Find() returns Row? (nullable reference, null on miss).

API and ABI breaking changes

This change is non-breaking with respect to row type kinds, because class/record table rows continue to work via RefUniqueIndex/ReadOnlyRefUniqueIndex while struct rows use UniqueIndex/ReadOnlyUniqueIndex.

API surface changes:

  • Generated Find() return type is now nullable (Row?) to correctly represent “missing”.

ABI/runtime:

  • Requires the point-scan hostcall import (datastore_index_scan_point_bsatn) to be available; the runtime uses point-scan for unique lookup (and point delete for unique delete).

Expected complexity level and risk

Low 2

Testing

  • Local testing: repro module + client validate view and direct Find() behavior

@rekhoff rekhoff self-assigned this Dec 19, 2025
@rekhoff rekhoff added this pull request to the merge queue Dec 19, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 19, 2025
@rekhoff rekhoff added this pull request to the merge queue Dec 19, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 19, 2025
@rekhoff rekhoff added this pull request to the merge queue Dec 20, 2025
Merged via the queue into master with commit 8a0cd87 Dec 20, 2025
55 of 58 checks passed
@rekhoff rekhoff deleted the rekhoff/add-datastore-index-scan-point-bsatn branch December 20, 2025 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Find() inside of View returning default value?

2 participants